package com.snail.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.snail.exception.BusinessException;
import com.snail.mapper.BizOrderMapper;
import com.snail.mapper.BizRefundRequestMapper;
import com.snail.model.dto.cart.RefundApplyDTO;
import com.snail.model.pojo.cart.BizOrder;
import com.snail.model.pojo.cart.BizRefundRequest;
import com.snail.model.vo.cart.RefundVO;
import com.snail.service.BizRefundRequestService;
import com.snail.utils.DynamicDateOrderRefundIdGenerator;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor = Exception.class)
public class BizRefundRequestServiceImpl implements BizRefundRequestService {

    // 缓存键前缀与过期时间配置
    private static final String CACHE_KEY_PREFIX = "refund:user:";
    private static final String LOCK_KEY_PREFIX = "lock:refund:";
    private static final long NORMAL_EXPIRE_SECONDS = 30 * 60;  // 30分钟
    private static final long ACTIVE_EXPIRE_SECONDS = 10 * 60;  // 10分钟（活跃状态）
    private static final long EMPTY_EXPIRE_SECONDS = 1 * 60;    // 1分钟（空结果）
    @Autowired
    private BizRefundRequestMapper  bizRefundRequestMapper;
    @Autowired
    private BizOrderMapper bizOrderMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ObjectMapper objectMapper;




    @Override
    public RefundVO applyRefund(RefundApplyDTO applyDTO) {
        // 1. 获取当前登录用户ID（实际项目从上下文获取）
        Long userId = applyDTO.getUserId();
        // 2. 查询订单
        BizOrder order = bizOrderMapper.selectByOrderNo(applyDTO.getOrderNo());
        if (order == null) {
            throw new BusinessException(400, "订单不存在");
        }
        // 3. 验证订单归属
        if (!userId.equals(order.getUserId())) {
//            throw new BusinessException("无权操作此订单");
            throw new BusinessException(400, "无权操作此订单");
        }
        // 4. 验证订单状态是否可退款
        if (!isRefundable(order.getOrderStatus())) {
//            throw new BusinessException("当前订单状态不支持退款");
            throw new BusinessException(400, "当前订单状态不支持退款");
        }
        // 5. 从订单中获取支付相关信息（假设订单表有这些字段）
        Integer paymentId = applyDTO.getPaymentId(); // 订单关联的支付ID
        BigDecimal payAmount = order.getPayAmount(); // 订单实付金额（假设订单表有此字段）
        if (payAmount == null || payAmount.compareTo(BigDecimal.ZERO) <= 0) {
//            throw new BusinessException("订单支付金额异常，无法退款");
            throw new BusinessException(400, "订单支付金额异常，无法退款");
        }
        // 6. 检查是否已有未完成的退款申请
        if (hasUnfinishedRefund(applyDTO.getOrderNo())) {
//            throw new BusinessException("已有未完成的退款申请，请稍后再试");
            throw new BusinessException(400, "已有未完成的退款申请，请稍后再试");
        }
        BigDecimal applyRefundAmount = applyDTO.getRefundAmount();
        if (applyRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException(400, "退款金额必须大于0");
        }
        if (applyRefundAmount.compareTo(payAmount) > 0) {
            throw new BusinessException(400, "退款金额不能超过订单实付金额");
        }
// 用用户申请的金额作为退款金额
        applyDTO.setRefundAmount(applyRefundAmount);
        /**
         * 生成退款单号
         */
        DynamicDateOrderRefundIdGenerator dynamicDateOrderRefundIdGenerator = new DynamicDateOrderRefundIdGenerator(3);
        String refundNo = dynamicDateOrderRefundIdGenerator.generateRefundId();
        // 7. 创建退款记录
        BizRefundRequest refund = new BizRefundRequest();
        refund.setOrderNo(applyDTO.getOrderNo());
        refund.setUserId(userId);
        refund.setRefundNo(refundNo);
        refund.setPaymentId(paymentId); // 从订单获取支付ID,看看是否需要这个字段
        refund.setRefundAmount(payAmount); // 从订单获取支付金额作为退款金额
        refund.setReason(applyDTO.getReason());
        refund.setStatus(1); // 1-申请中
        refund.setCreateTime(new Date());
        refund.setUpdateTime(new Date());
        // 保存退款记录
        bizRefundRequestMapper.insert(refund);
        // 8. 构建返回结果
        RefundVO vo = new RefundVO();
        vo.setRefundId(refund.getRefundId());
        vo.setRefundNo(refund.getRefundNo());
        vo.setStatus(refund.getStatus());
        return vo;
    }
    @Override
    public List<BizRefundRequest> getRefundRequestByUserId(Long userId) {
        if (userId == null) {
            return List.of();
        }
        String cacheKey = CACHE_KEY_PREFIX + userId;
        String lockKey = LOCK_KEY_PREFIX + userId;
        // 1. 尝试从缓存获取
        List<BizRefundRequest> cachedData = getCachedData(cacheKey);
        if (cachedData != null) {
            return cachedData;
        }
        // 2. 缓存未命中，获取分布式锁
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取锁，最多等待3秒，持有5秒
            if (lock.tryLock(3, 5, TimeUnit.SECONDS)) {
                // 双重检查缓存
                cachedData = getCachedData(cacheKey);
                if (cachedData != null) {
                    return cachedData;
                }

                // 3. 查询数据库（只返回必要字段）
                List<BizRefundRequest> dbData = bizRefundRequestMapper.getSimpleRefundByUserId(userId);
                System.out.println(dbData);

                // 4. 缓存数据（根据状态设置不同过期时间）
                cacheData(cacheKey, dbData);

                return dbData;
            } else {
                // 获取锁失败时，返回可能存在的旧缓存（保证可用性）
                return getCachedData(cacheKey) != null ? getCachedData(cacheKey) : List.of();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // 异常时直接查询数据库兜底
            return bizRefundRequestMapper.getSimpleRefundByUserId(userId);
        } finally {
            // 确保锁释放
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }

    /**
     * 从缓存获取数据并反序列化
     */
    private List<BizRefundRequest> getCachedData(String cacheKey) {
        try {
            Object data = redisTemplate.opsForValue().get(cacheKey);
            if (data instanceof String) {
                return objectMapper.readValue(
                        (String) data,
                        new TypeReference<List<BizRefundRequest>>() {}
                );
            }
        } catch (Exception e) {
            // 序列化失败时删除缓存
            redisTemplate.delete(cacheKey);
        }
        return null;
    }

    /**
     * 缓存数据到Redis，根据内容动态设置过期时间
     */
    private void cacheData(String cacheKey, List<BizRefundRequest> data) {
        try {
            if (CollectionUtils.isEmpty(data)) {
                // 空结果短期缓存
                redisTemplate.opsForValue().set(
                        cacheKey,
                        objectMapper.writeValueAsString(data),
                        EMPTY_EXPIRE_SECONDS,
                        TimeUnit.SECONDS
                );
            } else {
                // 判断是否包含活跃状态（待处理/退款中）
                boolean hasActive = data.stream()
                        .anyMatch(r -> "PENDING".equals(r.getStatus())
                                || "REFUNDING".equals(r.getStatus()));

                // 动态设置过期时间
                long expire = hasActive ? ACTIVE_EXPIRE_SECONDS : NORMAL_EXPIRE_SECONDS;
                redisTemplate.opsForValue().set(
                        cacheKey,
                        objectMapper.writeValueAsString(data),
                        expire,
                        TimeUnit.SECONDS
                );
            }
        } catch (Exception e) {
            // 缓存失败不影响主流程
            // log.error("缓存退款数据失败: {}", e.getMessage());
        }
    }

    @Override
    public void updateRefundRequest(BizRefundRequest refundRequest) {
        bizRefundRequestMapper.updateRefundRequest(refundRequest);
    }

    @Override
    public void quitRefundRequest(Long userId, Integer status) {
        bizRefundRequestMapper.quitRefundRequest(userId, status);
    }


    /**
     * 检查是否有未完成的退款申请
     */
    private boolean hasUnfinishedRefund(String orderNo) {
        // 假设Mapper中有此方法，查询状态为1（申请中）或2（已同意）的记录
        return bizRefundRequestMapper.countUnfinishedByOrderNo(orderNo) > 0;
    }




    /**
     * 判断订单是否可退款
     * 此处根据实际业务逻辑调整
     */
    private boolean isRefundable(Integer orderStatus) {
        // 例如：10-已支付，20-已发货 状态可退款
        return orderStatus == 10 || orderStatus == 20;
    }
}
